{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# CW-Husky Triggers\n",
    "\n",
    "This notebook shows how to use Husky's different trigger modules."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SCOPE=\"OPENADC\"\n",
    "PLATFORM = 'CW308_SAM4S'\n",
    "#PLATFORM = 'CW308_STM32F3'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import chipwhisperer as cw\n",
    "%run \"../../Setup_Scripts/Setup_Generic.ipynb\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This notebook should run on all simpleserial targets, but ADC triggering works better on this target firmware, on our STM32 target. (Unfortunately ADC triggering doesn't work very well on the SAM4S target because there is not as much distinction in the power trace between idle and busy.)\n",
    "\n",
    "Here we use a pre-compiled target firmware because the SAD parameters in particular are very much tied to the firmware.\n",
    "\n",
    "If you use a different target and/or firmware, you will need to adjust the triggering thresholds for the ADC and SAD sections."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cw.program_target(scope, prog, \"../../../firmware/mcu/simpleserial-trace/simpleserial-trace-{}.hex\".format(PLATFORM))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "reset_target(scope)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. UART Triggering\n",
    "\n",
    "It's possible to trigger on the UART data that is sent to or received from the target."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trigger.module = 'UART'\n",
    "scope.UARTTrigger.enabled = True\n",
    "scope.UARTTrigger.baud = 38400\n",
    "scope.gain.db = 12"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We set up two matching rules: rule 0 matches the plaintext that we'll send to the target on tio2, and rule 1 matches the response that the target will send back on tio1. Then we demonstrate triggering on each.\n",
    "\n",
    "The match patterns can be up to 8 bytes long. There is a bit-wise mask parameter which defaults to all ones. Up to 8 rules can be specified and selectively enabled."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.UARTTrigger.set_pattern_match(0, 'p00') # match 'p'... that we send\n",
    "scope.UARTTrigger.set_pattern_match(1, 'r7DF7') # match 'r'... that we receive"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's first trigger on the plaintext command that's sent to the target on tio2.\n",
    "\n",
    "The UART trigger module can use any trigger input that you can specify to the normal trigger module.\n",
    "\n",
    "If you have an oscilloscope or logic analyzer, probe tio2, tio1, and Trigger/Glitch Out MCX. The MCX is the internal ADC capture trigger. You use this to confirm that the trigger event occurs when expected.\n",
    "\n",
    "The trigger fires after the last pattern byte is received (i.e. in the case of the 'p00' pattern, after the second '0'). *This is different behaviour from earlier ChipWhisperer releases (5.7.0 and earlier), where the trigger would fire after 8 bytes are received (i.e. in the 'p00' example, it would fire after receiving 'p00' following by 5 \"don't care\" bytes).*\n",
    "\n",
    "In some situations you may wish for the trigger to fire later; for example if you want the trigger to fire after receiving any 8-byte pattern which starts with 'p0'. In this situation, specify the pattern and mask like this:\n",
    "\n",
    "```python\n",
    "scope.UARTTrigger.set_pattern_match(0, 'p0xxxxxx', mask=[255,255,0,0,0,0,0,0])\n",
    "```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trigger.triggers = 'tio2'\n",
    "scope.UARTTrigger.trigger_source = 0\n",
    "scope.UARTTrigger.rules_enabled = [0,1]\n",
    "scope.io.glitch_trig_mcx = 'trigger'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trace = cw.capture_trace(scope, target, bytearray(16), bytearray(16))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The UARTTrigger module records the actual UART data that caused the last trigger (useful in the case of wildcards), and a counter for each time a rule matched and triggered:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.UARTTrigger.matched_pattern_data(as_string=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.UARTTrigger.matched_pattern_counts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's switch over to triggering on the response on tio1. Again, we must specify on which pin the trigger module should use, and which rule should cause a trigger:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trigger.triggers = 'tio1'\n",
    "scope.UARTTrigger.trigger_source = 1\n",
    "scope.UARTTrigger.rules_enabled = [0,1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trace = cw.capture_trace(scope, target, bytearray(16), bytearray(16))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.UARTTrigger.matched_pattern_data(as_string=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.UARTTrigger.matched_pattern_counts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`scope.UARTTrigger` actually runs on the same FPGA logic as `scope.trace` aka TraceWhisperer (the ARM trace sniffer), because ARM trace in SWO mode is just UART.\n",
    "\n",
    "This means it's also possible to record all the UART data. Since the data is time-stamped (exactly like it is in TraceWhisperer), how much data can be recorded depends on its nature. There is space for up to 8192 bytes, but periods of inactivity will require some of that capacity to be used for additional timestamp storage.\n",
    "\n",
    "(If you're interested in using the timestamp data, the [TraceWhisperer.ipynb](https://github.com/newaetech/DesignStartTrace/blob/master/jupyter/TraceWhisperer.ipynb) notebook will show you how.)\n",
    "\n",
    "By triggering on `tio1 and tio2`, we'll capture both the Tx and Rx traffic:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trigger.triggers = 'tio1 and tio2'\n",
    "scope.UARTTrigger.trigger_source = 0\n",
    "scope.UARTTrigger.rules_enabled = [0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trace.trace_mode = 'swo'\n",
    "scope.trace.capture.mode = 'count_writes'\n",
    "scope.trace.capture.count = 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trace = cw.capture_trace(scope, target, bytearray(16), bytearray(16))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "raw = scope.trace.read_capture_data()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "len(raw)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`raw` is time-stamped raw UART data. There's a convenience method to decode it to ASCII:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ascii = scope.UARTTrigger.uart_data(raw)\n",
    "for b in ascii:\n",
    "    print(b, end='')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The UART trigger module supports issuing multiple triggers: *up to* `scope.UARTTrigger.capture.max_triggers` can be issued. Once this limit is reached, no more triggers are issued until the scope is re-armed.\n",
    "\n",
    "Finally, while these examples have used the usual tio1 and tio2 lines as inputs, you can use anything input that `scope.trigger.triggers` supports."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default, the `UARTTrigger` module is set for 8-N-1 operation, since these are the UART settings for ChipWhisperer targets.\n",
    "\n",
    "If you have a different target, you can adjust `scope.UARTTrigger.data_bits`, `scope.UARTTrigger.parity` and `scope.UARTTrigger.stop_bits` accordingly.\n",
    "\n",
    "If `data_bits` is not 8, the pattern and mask are to be provided as a list of `data_bits`-sized integers.\n",
    "\n",
    "If `parity` is used, the `scope.UARTTrigger.accept_parity_errors` provides the option of ignoring words with parity errors. When `scope.UARTTrigger.accept_parity_errors = True`, words with parity errors are accepted and pattern matching continues as though these words were received without error; when `scope.UARTTrigger.accept_parity_errors = False`, words with parity errors are ignored, as if they were never sent."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. ADC level triggering\n",
    "\n",
    "This one is a lot more simple: it simply triggers when ADC samples exceed a defined level.\n",
    "\n",
    "This can be useful when the target is idle and \"quiet\" outside of the target operation.\n",
    "\n",
    "We start by getting another power trace from \"normal\" triggering, and with proper gain and ADC settings, so we can observe how high the power samples go."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.gain.db = 12\n",
    "scope.adc.samples = 50000\n",
    "scope.adc.presamples = 10000\n",
    "scope.trigger.module = 'basic'\n",
    "scope.trigger.triggers = 'tio4'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trace = cw.capture_trace(scope, target, bytearray(16), bytearray(16))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.resources import INLINE\n",
    "from bokeh.io import output_notebook\n",
    "from bokeh.models import Span\n",
    "import numpy as np\n",
    "output_notebook(INLINE)\n",
    "\n",
    "o = figure(width=1200)\n",
    "\n",
    "xrange = list(range(len(trace.wave)))\n",
    "O = o.line(xrange, trace.wave)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show(o)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you're using the simpleserial-aes firmware on the STM32 target, you should see a large negative spike shortly after the start of the capture, which should be a good marker for level-based triggering:\n",
    "\n",
    "(On the SAM4S, there isn't much difference between the power when idle vs encrypting, so the ADC trigger isn't as useful there.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trigger.module = 'ADC'\n",
    "scope.trigger.level = min(trace.wave)*0.95"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.adc.presamples = int(np.where(trace.wave == min(trace.wave[:20000]))[0][0])+ 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "adc_triggered_trace = cw.capture_trace(scope, target, bytearray(16), bytearray(16))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "o = figure(width=1200)\n",
    "\n",
    "O1 = o.line(xrange, trace.wave, line_color='black')\n",
    "O2 = o.line(xrange, adc_triggered_trace.wave, line_color='blue')\n",
    "O3 = o.line(xrange, abs(trace.wave - adc_triggered_trace.wave), line_color='red')\n",
    "\n",
    "o.renderers.extend([Span(location=scope.trigger.level, dimension='width', line_color='green', line_width=2, line_dash='dashed')])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show(o)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The two traces should be coincident or very nearly so.\n",
    "\n",
    "With ADC-level triggering, a single trigger is issued: after the first trigger, no further triggers are issued, even if ADC samples exceed the level threshold, until the scope is re-armed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. SAD Triggering\n",
    "\n",
    "Sum-of-Absolute Differences (SAD) has grown to have enough features to require its own notebook: [06 - Husky SAD Triggering.ipynb](06%20-%20Husky%20SAD%20Triggering.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. TraceWhisperer\n",
    "\n",
    "Triggering from traces is covered in a separate notebook: [TraceWhisperer.ipynb](https://github.com/newaetech/DesignStartTrace/blob/master/jupyter/TraceWhisperer.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. Edge Count Triggering\n",
    "\n",
    "This counts rising and falling edges of an input signal and triggers after a specified number of edges.\n",
    "\n",
    "This can be very useful when you want to trigger from some protocol that Husky can't decode.\n",
    "\n",
    "Here we'll demonstrate with UART because it's what we have with our targets, but it can be used for much more than that.\n",
    "\n",
    "The input signal to the edge counter can be anything that `scope.trigger.triggers` supports (which, in addition to what you could use on CW-Lite/Pro, includes the USERIO data pins)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.default_setup()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trigger.module = 'edge_counter'\n",
    "scope.trigger.triggers = 'tio1'\n",
    "#scope.trigger.triggers = 'tio2'\n",
    "scope.trigger.edges = 3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll use Husky's logic analyzer to visualize the generated trigger. The trigger is a narrow single-cycle pulse of the ADC sampling clock, so we must sample fairly fast, which means we won't see all of the UART traffic."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trace.enabled = False\n",
    "scope.LA.enabled = True\n",
    "scope.LA.clk_source = 'pll'\n",
    "scope.LA.oversampling_factor = 4\n",
    "scope.LA.downsample = 1\n",
    "scope.LA.capture_group = 'CW 20-pin'\n",
    "scope.LA.trigger_source = 'falling_tio1'\n",
    "#scope.LA.trigger_source = 'falling_tio2'\n",
    "scope.LA.capture_depth = 16000\n",
    "scope.adc.clip_errors_disabled = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is so that the internal trigger can be captured by `scope.LA`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.io.glitch_trig_mcx = 'trigger'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.LA.arm()\n",
    "trace = cw.capture_trace(scope, target, bytearray(16), bytearray(16))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert not scope.LA.fifo_empty()\n",
    "raw = scope.LA.read_capture_data()\n",
    "tio1 = scope.LA.extract(raw, 0)\n",
    "tio2 = scope.LA.extract(raw, 1)\n",
    "trig = scope.LA.extract(raw, 7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bokeh.plotting import figure, show\n",
    "from bokeh.resources import INLINE\n",
    "from bokeh.io import output_notebook\n",
    "from bokeh.models import Span, Legend, LegendItem\n",
    "import numpy as np\n",
    "output_notebook(INLINE)\n",
    "\n",
    "o = figure(width=1800)\n",
    "\n",
    "xrange = list(range(len(tio1)))\n",
    "O1 = o.line(xrange, tio1 + 4, line_color='black')\n",
    "O2 = o.line(xrange, tio2 + 2, line_color='blue')\n",
    "O3 = o.line(xrange, trig + 0, line_color='red')\n",
    "\n",
    "legend = Legend(items=[\n",
    "    LegendItem(label='tio1', renderers=[O1]),\n",
    "    LegendItem(label='tio2', renderers=[O2]),\n",
    "    LegendItem(label='internal capture trigger', renderers=[O3]),\n",
    "])\n",
    "o.add_layout(legend)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "show(o)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first falling edge of TIO1 is missed by the `scope.LA` capture (because that's the edge which kicks off the capture).\n",
    "\n",
    "Accounting for that, you can see that the trigger occurs immediately after the third edge of TIO1.\n",
    "\n",
    "You can change `scope.trigger.edges` and see the trigger move around accordingly.\n",
    "\n",
    "If you set it higher than the number of edges that are observed, the capture will time out (because no trigger was generated). If this happens, `scope.trigger.edges_seen` will show how many edges were observed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.trigger.edges_seen"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Try the other trigger sources (don't forget to adjust `scope.LA.trigger_source` accordingly if you want to see the results via `scope.LA` as above)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Sequenced Triggers\n",
    "\n",
    "See [04 - Husky Trigger Sequencer.ipynb](04%20-%20Husky%20Trigger%20Sequencer.ipynb) to learn how to sequencer multiple trigger events."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (venv39)",
   "language": "python",
   "name": "venv39"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
